#include <NewSoftSerial.h>
#include <Wire.h>

#define DS1307_I2C_ADDRESS 0x68
#define D2 0
#define DS1307_START_RAM 0x8
#include "WProgram.h"
byte encodeAsBCD(byte value);
byte decodeBCD(byte value);
void setDateTime(byte hour, byte minute, byte second, byte day, byte month, byte year, byte dayOfWeek);
void getDateTime(byte *hour, byte *minute, byte *second, byte *day, byte *month, byte *year, byte *dayOfWeek);
void writeLongValueToAddress(unsigned long value, int address);
unsigned long readLongValueFromAddress(int address);
void dumpMemory();
void xbeeEnterCommandMode();
void xbeeEnterDataMode();
void xbeeIssueCommand(char *command);
void xbeeInitAndSetDestination(char *destination);
void processFlash();
void resetMeter();
void setup();
void loop();
volatile unsigned long counter;
NewSoftSerial xbeeSerial(5, 4); // pin 4 = RX, ping 5 = TX

/**
 * Encode a byte value as a Binary Coded Decimal.
 * @param value Value to encode
 * @return byte containing encoded value 
 */
byte encodeAsBCD(byte value)
{
    return ( (value/10*16) + (value%10) );
}

/**
 * Decode a byte containing a Binary Coded Decimal value.
 * @param value Value to decode
 * @return byte containing decoded value
 */
byte decodeBCD(byte value)
{
    return ( (value/16*10) + (value%16) );
}

/**
 * Sets the date on the DS1307 using 24 hour clock notation. This function performs no
 * error checking on passed values and therefore potentially non-sensical dates can be 
 *
 * @param hour byte value in the range 1-23
 * @param minute byte value in the range 0-59
 * @param second byte value in the range 0-59
 * @param day byte value in the range 1-31
 * @param month byte value in the range 1-12
 * @param year byte value in the range 0-99
 * @param dayOfWeek byte value in the range 1-7 with Sunday being day 7
 */
void setDateTime(byte hour, byte minute, byte second, byte day, byte month, byte year, byte dayOfWeek)
{
    Wire.beginTransmission(DS1307_I2C_ADDRESS);
    Wire.send(0);
    Wire.send(encodeAsBCD(second));    // 0 to bit 7 starts the clock
    Wire.send(encodeAsBCD(minute));
    Wire.send(encodeAsBCD(hour));
    Wire.send(encodeAsBCD(dayOfWeek));
    Wire.send(encodeAsBCD(day));
    Wire.send(encodeAsBCD(month));
    Wire.send(encodeAsBCD(year));
    Wire.endTransmission();
}

/**
 * Get the date from the DS1307. Is the getter version of the setDateTime function.
 *
 * @param hour pointer to a byte to store the hour 
 * @param minute pointer to a byte to store the minute
 * @param second pointer to a byte to store the second
 * @param day pointer to a byte to store the day
 * @param month pointer to a byte to store the month
 * @param year pointer to a byte to store the year
 * @param dayOfWeek pointer to a byte to store the day of the week
 */
void getDateTime(byte *hour, byte *minute, byte *second, byte *day, byte *month, byte *year, byte *dayOfWeek)
{
    Wire.beginTransmission(DS1307_I2C_ADDRESS);
    Wire.send(0);
    Wire.endTransmission();

    Wire.requestFrom(DS1307_I2C_ADDRESS, 7);

    // A few of these need masks because certain bits are control bits
    *hour = decodeBCD(Wire.receive() & 0x3f);
    *minute = decodeBCD(Wire.receive());
    *second = decodeBCD(Wire.receive() & 0x7f);
    *day = decodeBCD(Wire.receive());
    *month = decodeBCD(Wire.receive());
    *year = decodeBCD(Wire.receive());
    *dayOfWeek = decodeBCD(Wire.receive());

}

void writeLongValueToAddress(unsigned long value, int address)
{
    Wire.beginTransmission(DS1307_I2C_ADDRESS);
    Wire.send(address); // Set the register pointer to be just past the date/time registers.
    byte b = 0;
    //    Serial.print("Value:   [0x");
    //    Serial.print(value, HEX);
    //    Serial.print(", l");
    //    Serial.print(value);
    //    Serial.print("]\n");


    //    Serial.print("Writing: [");
    for(int i=0; i< 4; i++)
    {
        byte d = (value >> i*8) & 0xFF;
        //        Serial.print(d, HEX);
        Wire.send(d);
        //delay(100);
    }
    //    Serial.println("]");
    Wire.endTransmission();
}

unsigned long readLongValueFromAddress(int address)
{
    unsigned long result = 0;
    unsigned long b = 0;

    // Reset the register pointer
    Wire.beginTransmission(DS1307_I2C_ADDRESS);
    Wire.send(address);
    Wire.endTransmission();

    Wire.requestFrom(DS1307_I2C_ADDRESS, 4);
    for(int i =0; i<4; i++)
    {
        byte d = Wire.receive();
        b = (unsigned long)d<<(i*8);
        result = result | b;
    }
    //    Serial.print("\nRead: [");
    //    Serial.print(result);
    //    Serial.println("L]");
    return result;
}

void dumpMemory()
{
    // Reset the register pointer
    Wire.beginTransmission(DS1307_I2C_ADDRESS);
    Wire.send(0x0);
    Wire.endTransmission();

    Wire.requestFrom(DS1307_I2C_ADDRESS, 64);
    for(int i=0; i<=64; i++)
    {
        Serial.print("Address:[0x");
        Serial.print(i, HEX);
        Serial.print("] = 0x");
        byte b = Wire.receive();
        Serial.print(b, HEX);
        Serial.print("\n");
    }    
}

void xbeeEnterCommandMode()
{
    //put xbee in command mode
    Serial.println("> +++");
    xbeeSerial.print("+++");
    char thisByte = 0;
    Serial.print("< ");
    while(thisByte !='\r')
    {
        if(xbeeSerial.available()>0)
        {
            thisByte = xbeeSerial.read();
            Serial.print(thisByte);
        }
    }
 Serial.println("");   
}

void xbeeEnterDataMode()
{
    //put xbee in command mode
    Serial.println("> ATCN");
    xbeeSerial.print("ATCN\r");
    char thisByte = 0;
    Serial.print("< ");
    while(thisByte !='\r')
    {
        if(xbeeSerial.available()>0)
        {
            thisByte = xbeeSerial.read();
            Serial.print(thisByte);
        }
    }
     Serial.println("");
}

void xbeeIssueCommand(char *command)
{
    Serial.print(">");
    Serial.println(command);
    xbeeSerial.print(command);
    xbeeSerial.print("\r");
    Serial.print("< ");
    char thisByte = 0;
    while(thisByte !='\r')
    {
        if(xbeeSerial.available()>0)
        {
            thisByte = xbeeSerial.read();
            Serial.print(thisByte);
        }
    }
     Serial.println("");
}    

void xbeeInitAndSetDestination(char *destination)
{
    xbeeEnterCommandMode();
    
    xbeeIssueCommand("ATMY 1001");
    xbeeIssueCommand("ATDL 1000");
    //xbeeIssueCommand("ATDN base");
    xbeeIssueCommand("ATWR");
    
    xbeeEnterDataMode();
}

/**
 * The processFlash function is called when the interrupt is fired as a result of
 * D2 detecting a rising edge. In this function we increment the value stored in
 * the DS1307's memory.
 */
void processFlash()
{
    counter = counter +1;
    for(int i=0; i<5000; i++)
    {
        digitalWrite(8,HIGH);      
    }
    digitalWrite(8,LOW);
}

void resetMeter()
{
    byte second, minute, hour, day, month, year, dayOfWeek;

     second = 00;
     minute = 35;
     hour = 12;
     day = 4;
     month = 4;
     year = 9;
     dayOfWeek = 6;
     setDateTime(second, minute, hour, day, month, year, dayOfWeek);

     writeLongValueToAddress(0, DS1307_START_RAM);
}

void setup()
{
    Wire.begin();
    Serial.begin(9600);
    xbeeSerial.begin(9600);

    pinMode(8, OUTPUT);
    counter = readLongValueFromAddress(DS1307_START_RAM);
    attachInterrupt(D2, processFlash, RISING);

    xbeeInitAndSetDestination("base");
}



void loop()
{
    byte second, minute, hour, dayOfWeek, day, month, year;

    getDateTime(&second, &minute, &hour, &dayOfWeek, &day, &month, &year);

    Serial.print(hour, DEC);
    Serial.print(":");
    Serial.print(minute, DEC);
    Serial.print(":");
    Serial.print(second, DEC);
    Serial.print("  ");
    Serial.print(month, DEC);
    Serial.print("/");
    Serial.print(day, DEC);
    Serial.print("/");
    Serial.print(year, DEC);
    Serial.print("  Day_of_week:");
    Serial.print(dayOfWeek, DEC);

    Serial.print(" counter:[");
    Serial.print(readLongValueFromAddress(DS1307_START_RAM));
    Serial.println("]");
    delay(5000);
    writeLongValueToAddress(counter, DS1307_START_RAM);
    
    xbeeSerial.print(hour, DEC);
    xbeeSerial.print(":");
    xbeeSerial.print(minute, DEC);
    xbeeSerial.print(":");
    xbeeSerial.print(second, DEC);
    xbeeSerial.print("-");
    xbeeSerial.print(day, DEC);
    xbeeSerial.print("/");
    xbeeSerial.print(month, DEC);
    xbeeSerial.print("/");
    xbeeSerial.print(year, DEC);
    xbeeSerial.print(",");
    xbeeSerial.print(readLongValueFromAddress(DS1307_START_RAM));
    xbeeSerial.print(";\r");

}

int main(void)
{
	init();

	setup();
    
	for (;;)
		loop();
        
	return 0;
}

